home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright 1991,1992 Eric R. Smith.
- Copyright 1992,1993,1994 Atari Corporation.
- All rights reserved.
- */
-
- /* a VERY simple tosfs.c
- * this one is extremely brain-damaged, but will serve OK for a
- * skeleton in which to put a "real" tosfs.c
- */
-
- #include "mint.h"
-
- /* if NEWWAY is defined, tosfs uses the new dup_cookie/release_cookie
- * protocol to keep track of file cookies, instead of the old
- * method of "timing"
- */
- /* #define NEWWAY */
- #if 0
- #define COOKIE_DB(x) DEBUG(x)
- #else
- #define COOKIE_DB(x)
- #endif
-
- /* if RO_FASCISM is defined, the read/write modes are enforced. This is
- * a Good Thing, not fascist at all. Ask Allan Pratt why he chose
- * that name sometime.
- */
- #define RO_FASCISM
-
- /* temporary code for debugging Falcon media change bug */
- #if 0
- #define MEDIA_DB(x) DEBUG(x)
- #else
- #define MEDIA_DB(x)
- #endif
-
- /* search mask for anything OTHER THAN a volume label */
- #define FILEORDIR 0x37
-
- char tmpbuf[PATH_MAX+1];
-
- static long ARGS_ON_STACK tos_root P_((int drv, fcookie *fc));
- static long ARGS_ON_STACK tos_lookup P_((fcookie *dir, const char *name, fcookie *fc));
- static long ARGS_ON_STACK tos_getxattr P_((fcookie *fc, XATTR *xattr));
- static long ARGS_ON_STACK tos_chattr P_((fcookie *fc, int attrib));
- static long ARGS_ON_STACK tos_chown P_((fcookie *fc, int uid, int gid));
- static long ARGS_ON_STACK tos_chmode P_((fcookie *fc, unsigned mode));
- static long ARGS_ON_STACK tos_mkdir P_((fcookie *dir, const char *name, unsigned mode));
- static long ARGS_ON_STACK tos_rmdir P_((fcookie *dir, const char *name));
- static long ARGS_ON_STACK tos_remove P_((fcookie *dir, const char *name));
- static long ARGS_ON_STACK tos_getname P_((fcookie *root, fcookie *dir,
- char *pathname, int size));
- static long ARGS_ON_STACK tos_rename P_((fcookie *olddir, char *oldname,
- fcookie *newdir, const char *newname));
- static long ARGS_ON_STACK tos_opendir P_((DIR *dirh, int flags));
- static long ARGS_ON_STACK tos_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *));
- static long ARGS_ON_STACK tos_rewinddir P_((DIR *dirh));
- static long ARGS_ON_STACK tos_closedir P_((DIR *dirh));
- static long ARGS_ON_STACK tos_pathconf P_((fcookie *dir, int which));
- static long ARGS_ON_STACK tos_dfree P_((fcookie *dir, long *buf));
- static long ARGS_ON_STACK tos_writelabel P_((fcookie *dir, const char *name));
- static long ARGS_ON_STACK tos_readlabel P_((fcookie *dir, char *name, int namelen));
-
- static long ARGS_ON_STACK tos_creat P_((fcookie *dir, const char *name, unsigned mode,
- int attrib, fcookie *fc));
- static DEVDRV * ARGS_ON_STACK tos_getdev P_((fcookie *fc, long *devsp));
- static long ARGS_ON_STACK tos_open P_((FILEPTR *f));
- static long ARGS_ON_STACK tos_write P_((FILEPTR *f, const char *buf, long bytes));
- static long ARGS_ON_STACK tos_read P_((FILEPTR *f, char *buf, long bytes));
- static long ARGS_ON_STACK tos_lseek P_((FILEPTR *f, long where, int whence));
- static long ARGS_ON_STACK tos_ioctl P_((FILEPTR *f, int mode, void *buf));
- static long ARGS_ON_STACK tos_datime P_((FILEPTR *f, short *time, int rwflag));
- static long ARGS_ON_STACK tos_close P_((FILEPTR *f, int pid));
- static long ARGS_ON_STACK tos_dskchng P_((int drv));
-
- #ifdef NEWWAY
- static long ARGS_ON_STACK tos_release P_((fcookie *fc));
- static long ARGS_ON_STACK tos_dupcookie P_((fcookie *dst, fcookie *src));
- #endif
-
- /* some routines from biosfs.c */
- extern long ARGS_ON_STACK null_select P_((FILEPTR *f, long p, int mode));
- extern void ARGS_ON_STACK null_unselect P_((FILEPTR *f, long p, int mode));
-
- DEVDRV tos_device = {
- tos_open, tos_write, tos_read, tos_lseek, tos_ioctl, tos_datime,
- tos_close, null_select, null_unselect
- };
-
- FILESYS tos_filesys = {
- (FILESYS *)0,
- FS_KNOPARSE | FS_NOXBIT | FS_LONGPATH,
- tos_root,
- tos_lookup, tos_creat, tos_getdev, tos_getxattr,
- tos_chattr, tos_chown, tos_chmode,
- tos_mkdir, tos_rmdir, tos_remove, tos_getname, tos_rename,
- tos_opendir, tos_readdir, tos_rewinddir, tos_closedir,
- tos_pathconf, tos_dfree, tos_writelabel, tos_readlabel,
- nosymlink, noreadlink, nohardlink, nofscntl, tos_dskchng,
- #ifdef NEWWAY
- tos_release, tos_dupcookie
- #else
- 0, 0
- #endif
- };
-
- /* some utility functions and variables: see end of file */
- static DTABUF *lastdta; /* last DTA buffer we asked TOS about */
- static DTABUF foo;
- static void do_setdta P_((DTABUF *dta));
- static int executable_extension P_((char *));
-
- /* this array keeps track of which drives have been changed */
- /* a nonzero entry means that the corresponding drive has been changed,
- * but GEMDOS doesn't know it yet
- */
- static char drvchanged[NUM_DRIVES];
-
- /* force TOS to see a media change */
- static void force_mediach P_((int drv));
- static long ARGS_ON_STACK Newgetbpb P_((int));
- static long ARGS_ON_STACK Newmediach P_((int));
- static long ARGS_ON_STACK Newrwabs P_((int, void *, int, int, int, long));
-
- #ifdef NEWWAY
- #define NUM_INDICES 64
- #else
- #define NUM_INDICES 128
- #define MIN_AGE 8
- #endif
-
- struct tindex {
- char *name; /* full path name */
- FILEPTR *open; /* fileptrs for this file; OR
- * count of number of open directories
- */
- LOCK *locks; /* locks on this file */
- /* file status */
- long size;
- short time;
- short date;
- short attr;
- short valid; /* 1 if the above status is still valid */
- #ifdef NEWWAY
- short links; /* how many times index is in use */
- #else
- short stamp; /* age of this index, for garbage collection */
- #endif
- } gl_ti[NUM_INDICES];
-
- /* temporary index for files found by readdir */
- static struct tindex tmpindex;
- static char tmpiname[PATH_MAX];
-
- static struct tindex *tstrindex P_((char *s));
- static int tfullpath P_((char *result, struct tindex *base, const char *name));
- static struct tindex *garbage_collect P_((void));
-
- #ifndef NEWWAY
- static short tclock; /* #calls to tfullpath since last garbage
- collection */
- #endif
-
- /* some extra flags for the attr field */
-
- /*
- * is a string the name of a file with executable extension?
- */
- #define FA_EXEC 0x4000
- /*
- * should the file be deleted when it is closed?
- */
- #define FA_DELETE 0x2000
-
- /*
- * NOTE: call executable_extension only on a DTA name returned from
- * Fsfirst(), not on an arbitrary path, for two reasons: (1) it
- * expects only upper case, and (2) it looks only for the 1st extension,
- * so a folder with a '.' in its name would confuse it.
- */
-
- static int
- executable_extension(s)
- char *s;
- {
- while (*s && *s != '.') s++;
- if (!*s) return 0;
- s++;
- if (s[0] == 'T') {
- return (s[1] == 'T' && s[2] == 'P') ||
- (s[1] == 'O' && s[2] == 'S');
- }
- if (s[0] == 'P')
- return s[1] == 'R' && s[2] == 'G';
- if (s[0] == 'A')
- return s[1] == 'P' && s[2] == 'P';
- if (s[0] == 'G')
- return s[1] == 'T' && s[2] == 'P';
- return 0;
- }
-
- /*
- * Look in the table of tos indices to see if an index corresponding
- * to this file name already exists. If so, mark it as being used
- * and return it. If not, find an empty slot and make an index for
- * this string. If no empty slots exist, garbage collect and
- * try again.
- *
- * This routine is pretty dumb; we really should use a hash table
- * of some sort
- */
-
- static struct tindex *tstrindex(s)
- char *s;
- {
- int i;
- char *r;
- struct tindex *t, *free = 0;
-
- assert(s != 0);
- t = gl_ti;
- for (i = 0; i < NUM_INDICES; i++, t++) {
- if (t->name && !stricmp(t->name, s)) {
- #ifndef NEWWAY
- t->stamp = tclock; /* update use time */
- #endif
- return t;
- }
- else if (!t->name && !free)
- free = t;
- }
- if (!free) {
- free = garbage_collect();
- }
- #ifdef NEWWAY
- if (!free) {
- FORCE("tosfs: all slots in use!!");
- FORCE("Links\tName");
- t = gl_ti;
- for (i = 0; i < NUM_INDICES; i++,t++) {
- FORCE("%d\t%s", t->links, t->name);
- }
- FATAL("tosfs: unable to get a file name index");
- }
- #else
- if (!free) {
- FATAL("tosfs: unable to get a file name index");
- }
- #endif
- r = kmalloc((long)strlen(s)+1);
- if (!r) {
- FATAL("tosfs: unable to allocate space for a file name");
- }
- strcpy(r, s);
- free->name = r;
- #ifdef NEWWAY
- free->links = 0;
- #else
- free->stamp = tclock;
- #endif
- free->open = 0;
- free->locks = 0;
-
- /* check to see if this file was recently returned by opendir() */
- #ifndef NEWWAY
- if (tmpindex.valid && tclock - tmpindex.stamp < MIN_AGE &&
- !stricmp(free->name, tmpindex.name)) {
- free->size = tmpindex.size;
- free->time = tmpindex.time;
- free->date = tmpindex.date;
- free->attr = tmpindex.attr;
- free->valid = 1;
- tmpindex.valid = 0;
- } else
- #endif
- free->valid = 0;
- return free;
- }
-
- /*
- * garbage collection routine: for any TOS index older than MIN_AGE,
- * check through all current processes to see if it's in use. If
- * not, free the corresponding string.
- * Returns: a pointer to a newly freed index, or NULL.
- */
-
- /* it's unlikely that the kernel would need to hold onto a file cookie
- for longer than this many calls to tstrindex() without first
- saving the cookie in a directory or file pointer
- */
-
- static struct tindex *
- garbage_collect()
- {
- struct tindex *free, *t;
- int i;
- #ifndef NEWWAY
- fcookie *fc, *gc;
- PROC *p;
- int j;
- int age;
- #endif
-
- free = 0;
- t = gl_ti;
- for (i = 0; i < NUM_INDICES; i++,t++) {
- if (!t->name) continue;
- #ifdef NEWWAY
- if (t->links == 0) {
- kfree(t->name);
- t->name = 0;
- if (!free) free = t;
- }
- #else
- age = tclock - t->stamp;
- t->stamp = 0;
- assert(age >= 0);
- if (age > MIN_AGE) {
- /* see if any process is using this index */
- if (t->open)
- goto found_index;
- for (p = proclist; p; p = p->gl_next) {
- fc = p->curdir;
- gc = p->root;
- for (j = 0; j < NUM_DRIVES; j++,fc++,gc++) {
- if (( fc->fs == &tos_filesys &&
- fc->index == (long)t ) ||
- ( gc->fs == &tos_filesys &&
- gc->index == (long)t ) )
- goto found_index;
- }
- }
- /* here, we couldn't find the index in use by any proc. */
- kfree(t->name);
- t->name = 0;
- if (!free)
- free = t;
- found_index:
- ;
- } else {
- /* make sure that future garbage collections might look at this file */
- t->stamp = -age;
- }
- #endif
- }
-
- #ifndef NEWWAY
- tclock = 0; /* reset the clock */
- tmpindex.valid = 0; /* expire the temporary Fsfirst buffer */
- #endif
- return free;
- }
-
- #define DIRSEP(c) ((c) == '\\')
-
- static int
- tfullpath(result, basei, path)
- char *result;
- struct tindex *basei;
- const char *path;
- {
- #define TNMTEMP 32
- char *n, name[TNMTEMP+1];
- int namelen, pathlen;
- char *base = basei->name;
- int r = 0;
-
- #ifndef NEWWAY
- basei->stamp = ++tclock;
- if (tclock > 10000) {
- /* garbage collect every so often whether we need it or not */
- (void)garbage_collect();
- }
- #endif
- if (!*path) {
- strncpy(result, base, PATH_MAX-1);
- return r;
- }
-
- strncpy(result, base, PATH_MAX-1);
-
- pathlen = strlen(result);
-
- /* now path is relative to what's currently in "result" */
-
- while(*path) {
- /* get next name in path */
- n = name; namelen = 0;
- while (*path && !DIRSEP(*path)) {
- /* BUG: we really should to the translation to DOS 8.3
- * format *here*, so that really long names are truncated
- * correctly.
- */
- if (namelen < TNMTEMP) {
- *n++ = toupper(*path); path++; namelen++;
- }
- else
- path++;
- }
- *n = 0;
- while (DIRSEP(*path)) path++;
- /* check for "." and ".." */
- if (!strcmp(name, ".")) continue;
- if (!strcmp(name, "..")) {
- n = strrchr(result, '\\');
- if (n) {
- *n = 0;
- pathlen = (int)(n - result);
- }
- else r = EMOUNT;
- continue;
- }
- if (pathlen + namelen < PATH_MAX - 1) {
- strcat(result, "\\");
- pathlen++;
-
- /* make sure the name is restricted to DOS 8.3 format */
- for (base = result; *base; base++)
- ;
- n = name;
- namelen = 0;
- while (*n && *n != '.' && namelen++ < 8) {
- *base++ = *n++;
- pathlen++;
- }
- while (*n && *n != '.') n++;
- if (*n == '.' && *(n+1) != 0) {
- *base++ = *n++;
- pathlen++;
- namelen = 0;
- while (*n && namelen++ < 3) {
- *base++ = *n++;
- pathlen++;
- }
- }
- *base = 0;
- }
- }
- return r;
- }
-
- static long ARGS_ON_STACK
- tos_root(drv, fc)
- int drv;
- fcookie *fc;
- {
- struct tindex *ti;
-
- ksprintf(tmpbuf, "%c:", drv+'A');
- fc->fs = &tos_filesys;
- fc->dev = drv;
- ti = tstrindex(tmpbuf);
- ti->size = ti->date = ti->time = 0;
- ti->attr = FA_DIR;
- ti->valid = 1;
- fc->index = (long)ti;
-
- /* if the drive has changed, make sure GEMDOS knows it! */
- if (drvchanged[drv]) {
- force_mediach(drv);
- }
- #ifdef NEWWAY
- ti->links++;
- #endif
- return 0;
- }
-
- static long ARGS_ON_STACK
- tos_lookup(dir, name, fc)
- fcookie *dir;
- const char *name;
- fcookie *fc;
- {
- long r;
- struct tindex *ti = (struct tindex *)dir->index;
-
- r = tfullpath(tmpbuf, ti, name);
-
- /* if the name is empty or otherwise trivial, just return the directory */
- if (!strcmp(ti->name, tmpbuf)) {
- *fc = *dir;
- #ifdef NEWWAY
- ti->links++;
- COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));
- #endif
- return r;
- }
-
- /* is there already an index for this file?? If so, is it up to date?? */
- ti = tstrindex(tmpbuf);
- if (!ti->valid) {
- if (tmpbuf[1] == ':' && tmpbuf[2] == 0) {
- /* a root directory -- lookup always succeeds */
- foo.dta_size = 0;
- foo.dta_date = foo.dta_time = 0;
- foo.dta_attrib = FA_DIR;
- foo.dta_name[0] = 0;
- } else {
- do_setdta(&foo);
- r = Fsfirst(tmpbuf, FILEORDIR);
- if (r) {
- DEBUG(("tos_lookup: Fsfirst(%s) returned %ld", tmpbuf, r));
- return r;
- }
- }
- ti->size = foo.dta_size;
- ti->date = foo.dta_date;
- ti->time = foo.dta_time;
- ti->attr = foo.dta_attrib;
- if (executable_extension(foo.dta_name))
- ti->attr |= FA_EXEC;
- ti->valid = 1;
- }
- fc->fs = &tos_filesys;
- fc->index = (long)ti;
- fc->dev = dir->dev;
- #ifdef NEWWAY
- ti->links++;
- COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));
- #endif
- return r;
- }
-
- static long ARGS_ON_STACK
- tos_getxattr(fc, xattr)
- fcookie *fc;
- XATTR *xattr;
- {
- struct tindex *ti = (struct tindex *)fc->index;
- long r;
- static long junkindex = 0;
-
- xattr->index = junkindex++;
- xattr->dev = fc->dev;
- xattr->rdev = fc->dev;
- xattr->nlink = 1;
- xattr->uid = xattr->gid = 0;
-
- #ifndef NEWWAY
- ti->stamp = ++tclock;
- #endif
-
- if (!ti->valid) {
- do_setdta(&foo);
- if (ti->name[2] == 0) { /* a root directory */
- /* actually, this can also happen if a program tries to open a file
- * with an empty name... so we should fail gracefully
- */
- ti->attr = FA_DIR;
- ti->size = 0;
- ti->date = ti->time = 0;
- goto around;
- }
-
- r = Fsfirst(ti->name, FILEORDIR);
- if (r) {
- DEBUG(("tosfs: search error %ld on [%s]", r, ti->name));
- return r;
- }
- ti->size = foo.dta_size;
- ti->date = foo.dta_date;
- ti->time = foo.dta_time;
- ti->attr = foo.dta_attrib;
- if (executable_extension(foo.dta_name))
- ti->attr |= FA_EXEC;
- around:
- ti->valid = 1;
- }
- xattr->size = ti->size;
-
- /* BUG: blksize isn't accurate if the sector size is not 512 */
- xattr->blksize = 1024;
- xattr->nblocks = (xattr->size + 1023) / 1024;
- xattr->mdate = xattr->cdate = xattr->adate = ti->date;
- xattr->mtime = xattr->ctime = xattr->atime = ti->time;
- xattr->mode = (ti->attr & FA_DIR) ? (S_IFDIR | DEFAULT_DIRMODE) :
- (S_IFREG | DEFAULT_MODE);
-
- /* TOS files have permissions rwxrwx--- */
- xattr->mode &= ~(S_IROTH|S_IWOTH|S_IXOTH);
-
- if (ti->attr & FA_RDONLY) {
- xattr->mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
- }
-
- if (ti->attr & FA_EXEC) {
- xattr->mode |= (S_IXUSR|S_IXGRP|S_IXOTH);
- }
- xattr->attr = ti->attr & 0xff;
- return 0;
- }
-
- static long ARGS_ON_STACK
- tos_chattr(fc, attrib)
- fcookie *fc;
- int attrib;
- {
- struct tindex *ti = (struct tindex *)fc->index;
-
- if (ti->attr & FA_DIR) {
- DEBUG(("error: attempt to change attributes of a directory"));
- return EACCDN;
- }
- ti->valid = 0;
- (void)tfullpath(tmpbuf, ti, "");
- return Fattrib(tmpbuf, 1, attrib);
- }
-
- static long ARGS_ON_STACK
- tos_chown(dir, uid, gid)
- fcookie *dir;
- int uid, gid;
- {
- UNUSED(dir); UNUSED(uid); UNUSED(gid);
- return EINVFN;
- }
-
- static long ARGS_ON_STACK
- tos_chmode(fc, mode)
- fcookie *fc;
- unsigned mode;
- {
- int oldattr, newattr;
- long r;
- struct tindex *ti = (struct tindex *)fc->index;
-
- oldattr = Fattrib(ti->name, 0, 0);
- if (oldattr < 0)
- return oldattr;
-
- ti->valid = 0;
-
- if (!(mode & S_IWUSR))
- newattr = oldattr | FA_RDONLY;
- else
- newattr = oldattr & ~FA_RDONLY;
- if (newattr != oldattr)
- r = Fattrib(ti->name, 1, newattr);
- else
- r = 0;
- return (r < 0) ? r : 0;
- }
-
- static long ARGS_ON_STACK
- tos_mkdir(dir, name, mode)
- fcookie *dir;
- const char *name;
- unsigned mode; /* ignored under TOS */
- {
- UNUSED(mode);
-
- (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
- tmpindex.valid = 0;
-
- return Dcreate(tmpbuf);
- }
-
- static long ARGS_ON_STACK
- tos_rmdir(dir, name)
- fcookie *dir;
- const char *name;
- {
- struct tindex *ti;
-
- (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
- ti = tstrindex(tmpbuf);
- ti->valid = 0;
-
- return Ddelete(tmpbuf);
- }
-
- static long ARGS_ON_STACK
- tos_remove(dir, name)
- fcookie *dir;
- const char *name;
- {
- struct tindex *ti;
-
- (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
-
- ti = tstrindex(tmpbuf);
- if (ti->open) {
- DEBUG(("tos_remove: file is open, will be deleted later"));
- if (ti->attr & FA_RDONLY)
- return EACCDN;
- ti->attr |= FA_DELETE;
- return 0;
- }
- ti->valid = 0;
- return Fdelete(tmpbuf);
- }
-
- static long ARGS_ON_STACK
- tos_getname(root, dir, pathname, size)
- fcookie *root, *dir;
- char *pathname;
- int size;
- {
- char *rootnam = ((struct tindex *)root->index)->name;
- char *dirnam = ((struct tindex *)dir->index)->name;
- int i;
-
- i = strlen(rootnam);
- if (strlen(dirnam) < i) {
- DEBUG(("tos_getname: root is longer than path"));
- return EINTRN;
- }
- if (strlen(dirnam+i) < size) {
- strcpy(pathname, dirnam + i);
- /*
- * BUG: must be a better way to decide upper/lower case
- */
- if (curproc->domain == DOM_MINT)
- strlwr(pathname);
- return 0;
- } else {
- DEBUG(("tosfs: name too long"));
- return ERANGE;
- }
- }
-
- static long ARGS_ON_STACK
- tos_rename(olddir, oldname, newdir, newname)
- fcookie *olddir;
- char *oldname;
- fcookie *newdir;
- const char *newname;
- {
- char newbuf[128];
- struct tindex *ti;
- long r;
-
- (void)tfullpath(tmpbuf, (struct tindex *)olddir->index, oldname);
- (void)tfullpath(newbuf, (struct tindex *)newdir->index, newname);
- r = Frename(0, tmpbuf, newbuf);
- if (r == 0) {
- ti = tstrindex(tmpbuf);
- kfree(ti->name);
- ti->name = kmalloc((long)strlen(newbuf)+1);
- if (!ti->name) {
- FATAL("tosfs: unable to allocate space for a name");
- }
- strcpy(ti->name, newbuf);
- ti->valid = 0;
- }
- return r;
- }
-
- #define DIR_FLAG(x) (x->fsstuff[0])
- #define STARTSEARCH 0 /* opendir() was just called */
- #define INSEARCH 1 /* readdir() has been called at least once */
- #define NMFILE 2 /* no more files to read */
-
- #define DIR_DTA(x) ((DTABUF *)(x->fsstuff + 2))
- #define DIR_NAME(x) (x->fsstuff + 32)
-
- /*
- * The directory functions are a bit tricky. What we do is have
- * opendir() do Fsfirst; the first readdir() picks up this name,
- * subsequent readdir()'s have to do Fsnext
- */
-
- static long ARGS_ON_STACK
- tos_opendir(dirh, flags)
- DIR *dirh;
- int flags;
- {
- long r;
- struct tindex *t = (struct tindex *)dirh->fc.index;
-
- UNUSED(flags);
-
- (void)tfullpath(tmpbuf, t, "*.*");
-
- do_setdta(DIR_DTA(dirh));
-
- r = Fsfirst(tmpbuf, FILEORDIR);
-
- if (r == 0) {
- t->open++;
- DIR_FLAG(dirh) = STARTSEARCH;
- return 0;
- } else if (r == EFILNF) {
- t->open++;
- DIR_FLAG(dirh) = NMFILE;
- return 0;
- }
- return r;
- }
-
- static long ARGS_ON_STACK
- tos_readdir(dirh, name, namelen, fc)
- DIR *dirh;
- char *name;
- int namelen;
- fcookie *fc;
- {
- static long index = 0;
- long ret;
- int giveindex = dirh->flags == 0;
- struct tindex *ti;
- DTABUF *dta = DIR_DTA(dirh);
-
- again:
- if (DIR_FLAG(dirh) == NMFILE)
- return ENMFIL;
-
- if (DIR_FLAG(dirh) == STARTSEARCH) {
- DIR_FLAG(dirh) = INSEARCH;
- } else {
- assert(DIR_FLAG(dirh) == INSEARCH);
- do_setdta(dta);
- ret = Fsnext();
- if (ret) {
- DIR_FLAG(dirh) = NMFILE;
- return ret;
- }
- }
-
- /* don't return volume labels from readdir */
- if (dta->dta_attrib == FA_LABEL) goto again;
-
- fc->fs = &tos_filesys;
- fc->dev = dirh->fc.dev;
-
- (void)tfullpath(tmpiname, (struct tindex *)dirh->fc.index, DIR_NAME(dirh));
-
- ti = &tmpindex;
- ti->name = tmpiname;
- ti->valid = 1;
- ti->size = dta->dta_size;
- ti->date = dta->dta_date;
- ti->time = dta->dta_time;
- ti->attr = dta->dta_attrib;
- #ifndef NEWWAY
- ti->stamp = tclock;
- #endif
- if (executable_extension(dta->dta_name))
- ti->attr |= FA_EXEC;
- fc->index = (long)ti;
-
- if (giveindex) {
- namelen -= (int) sizeof(long);
- if (namelen <= 0) return ERANGE;
- *((long *)name) = index++;
- name += sizeof(long);
- }
- strncpy(name, DIR_NAME(dirh), namelen-1);
- name[namelen-1] = 0;
-
- /* BUG: we really should do the "strlwr" operation only
- * for Dreaddir (i.e. if giveindex == 0) but
- * unfortunately some old programs rely on the behaviour
- * below
- */
- if (curproc->domain == DOM_MINT) {
- strlwr(name);
- }
- if (strlen(DIR_NAME(dirh)) >= namelen)
- return ENAMETOOLONG;
- #ifdef NEWWAY
- ti->links++;
- COOKIE_DB(("tos_readdir: %s now has %d links", ti->name, ti->links));
- #endif
- return 0;
- }
-
- static long ARGS_ON_STACK
- tos_rewinddir(dirh)
- DIR *dirh;
- {
- struct tindex *ti = (struct tindex *)dirh->fc.index;
- long r;
-
- (void)tfullpath(tmpbuf, ti, "*.*");
- do_setdta(DIR_DTA(dirh));
- r = Fsfirst(tmpbuf, FILEORDIR);
- if (r == 0) {
- DIR_FLAG(dirh) = STARTSEARCH;
- } else {
- DIR_FLAG(dirh) = NMFILE;
- }
- return r;
- }
-
- static long ARGS_ON_STACK
- tos_closedir(dirh)
- DIR *dirh;
- {
- struct tindex *t = (struct tindex *)dirh->fc.index;
-
- if (t->open == 0) {
- FATAL("t->open == 0: directory == %s", t->name);
- }
- --t->open;
- DIR_FLAG(dirh) = NMFILE;
- return 0;
- }
-
- static long ARGS_ON_STACK
- tos_pathconf(dir, which)
- fcookie *dir;
- int which;
- {
- UNUSED(dir);
-
- switch(which) {
- case -1:
- return DP_MAXREQ;
- case DP_IOPEN:
- return 60; /* we can only keep about this many open */
- case DP_MAXLINKS:
- return 1; /* no hard links */
- case DP_PATHMAX:
- return PATH_MAX;
- case DP_NAMEMAX:
- return 8+3+1;
- case DP_ATOMIC:
- return 512; /* we can write at least a sector atomically */
- case DP_TRUNC:
- return DP_DOSTRUNC; /* DOS style file names */
- case DP_CASE:
- return DP_CASECONV; /* names converted to upper case */
- default:
- return EINVFN;
- }
- }
-
- long ARGS_ON_STACK
- tos_dfree(dir, buf)
- fcookie *dir;
- long *buf;
- {
- return Dfree(buf, (dir->dev)+1);
- }
-
- /*
- * writelabel: creates a volume label
- * readlabel: reads a volume label
- * both of these are only guaranteed to work in the root directory
- */
-
- /*
- * BUG: this should first delete any old labels, so that it will
- * work with TOS <1.4
- */
-
- long ARGS_ON_STACK
- tos_writelabel(dir, name)
- fcookie *dir;
- const char *name;
- {
- long r;
- struct tindex *ti;
-
- (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
- r = Fcreate(tmpbuf, FA_LABEL);
- if (r < 0) return r;
- (void)Fclose((int)r);
- ti = tstrindex(tmpbuf);
- ti->valid = 0;
- return 0;
- }
-
- long ARGS_ON_STACK
- tos_readlabel(dir, name, namelen)
- fcookie *dir;
- char *name;
- int namelen;
- {
- long r;
- struct tindex *ti = (struct tindex *)dir->index;
-
- if (ti->name[2] != 0) /* not a root directory? */
- return EFILNF;
-
- (void)tfullpath(tmpbuf, ti, "*.*");
- do_setdta(&foo);
- r = Fsfirst(tmpbuf, FA_LABEL);
- if (r)
- return r;
- strncpy(name, foo.dta_name, namelen-1);
- return (strlen(foo.dta_name) < namelen) ? 0 : ENAMETOOLONG;
- }
-
- /*
- * TOS creat: this doesn't actually create the file, rather it
- * sets up a (fake) index for the file that will be used by
- * the later tos_open call.
- */
-
- static long ARGS_ON_STACK
- tos_creat(dir, name, mode, attrib, fc)
- fcookie *dir;
- const char *name;
- unsigned mode;
- int attrib;
- fcookie *fc;
- {
- struct tindex *ti;
- UNUSED(mode);
-
- (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
-
- ti = tstrindex(tmpbuf);
- ti->size = 0;
- ti->date = datestamp;
- ti->time = timestamp;
- ti->attr = attrib;
- ti->valid = 1;
-
- fc->fs = &tos_filesys;
- fc->index = (long)ti;
- fc->dev = dir->dev;
- #ifdef NEWWAY
- ti->links++;
- COOKIE_DB(("tos_creat: %s now has %d links", ti->name, ti->links));
- #endif
- return 0;
- }
-
- /*
- * TOS device driver
- */
-
- static DEVDRV * ARGS_ON_STACK
- tos_getdev(fc, devsp)
- fcookie *fc;
- long *devsp;
- {
- UNUSED(fc); UNUSED(devsp);
- return &tos_device;
- }
-
- static long ARGS_ON_STACK
- tos_open(f)
- FILEPTR *f;
- {
- struct tindex *ti;
- int mode = f->flags;
- int tosmode;
- long r;
- extern int flk; /* in main.c, set if _FLK cookie already present */
-
- ti = (struct tindex *)(f->fc.index);
- assert(ti != 0);
-
- #ifndef NEWWAY
- ti->stamp = ++tclock;
- #endif
- ti->valid = 0;
-
- #ifndef RO_FASCISM
- /* TEMPORARY HACK: change all modes to O_RDWR for files opened in
- * compatibility sharing mode. This is silly, but
- * allows broken TOS programs that write to read-only handles to continue
- * to work (it also helps file sharing, by making the realistic assumption
- * that any open TOS file can be written to). Eventually,
- * this should be tuneable by the user somehow.
- * ALSO: change O_COMPAT opens into O_DENYNONE; again, this may be temporary.
- */
- if ( (mode & O_SHMODE) == O_COMPAT ) {
- f->flags = (mode & ~(O_RWMODE|O_SHMODE)) | O_RDWR | O_DENYNONE;
- }
- #endif
-
- /* check to see that nobody has opened this file already in an
- * incompatible mode
- */
- if (denyshare(ti->open, f)) {
- TRACE(("tos_open: file sharing denied"));
- return EACCDN;
- }
-
- /*
- * now open the file; if O_TRUNC was specified, actually
- * create the file anew.
- * BUG: O_TRUNC without O_CREAT doesn't work right. The kernel doesn't
- * use this mode, anyways
- */
-
- if (mode & O_TRUNC) {
- if (ti->open) {
- DEBUG(("tos_open: attempt to truncate an open file"));
- return EACCDN;
- }
- r = Fcreate(ti->name, ti->attr);
- } else {
- if (flk)
- tosmode = mode & (O_RWMODE|O_SHMODE);
- else
- tosmode = (mode & O_RWMODE);
- if (tosmode == O_EXEC) tosmode = O_RDONLY;
-
- r = Fopen(ti->name, tosmode );
- if (r == EFILNF && (mode & O_CREAT))
- r = Fcreate(ti->name, ti->attr);
- }
-
- if (r < 0) {
- /* get rid of the index for the file, since it doesn't exist */
- kfree(ti->name);
- ti->name = 0;
- ti->valid = 0;
- return r;
- }
-
- f->devinfo = r;
-
- f->next = ti->open;
- ti->open = f;
- return 0;
- }
-
- static long ARGS_ON_STACK
- tos_write(f, buf, bytes)
- FILEPTR *f; const char *buf; long bytes;
- {
- struct tindex *ti = (struct tindex *)f->fc.index;
-
- ti->valid = 0;
- return Fwrite((int)f->devinfo, bytes, buf);
- }
-
- static long ARGS_ON_STACK
- tos_read(f, buf, bytes)
- FILEPTR *f; char *buf; long bytes;
- {
- return Fread((int)f->devinfo, bytes, buf);
- }
-
- static long ARGS_ON_STACK
- tos_lseek(f, where, whence)
- FILEPTR *f; long where; int whence;
- {
- long r;
-
- r = Fseek(where, (int)f->devinfo, whence);
- return r;
- }
-
- static long ARGS_ON_STACK
- tos_ioctl(f, mode, buf)
- FILEPTR *f; int mode; void *buf;
- {
- LOCK t, *lck, **old;
- struct flock *fl;
- long r;
- struct tindex *ti;
- extern int flk; /* set in main.c if _FLK already installed */
-
- if (mode == FIONREAD || mode == FIONWRITE) {
- *((long *)buf) = 1;
- return 0;
- }
- else if (mode == F_SETLK || mode == F_SETLKW || mode == F_GETLK) {
- fl = ((struct flock *)buf);
- t.l = *fl;
- switch(t.l.l_whence) {
- case 0:
- break;
- case 1: /* SEEK_CUR */
- r = Fseek(0L, (int)f->devinfo, 1);
- t.l.l_start += r;
- break;
- case 2:
- r = Fseek(0L, (int)f->devinfo, 1);
- t.l.l_start = Fseek(t.l.l_start, (int)f->devinfo, 2);
- (void)Fseek(r, (int)f->devinfo, 0);
- break;
- default:
- DEBUG(("Invalid value for l_whence"));
- return EINVFN;
- }
- /* BUG: can't lock a file starting at >2gigabytes from the beginning */
- if (t.l.l_start < 0) t.l.l_start = 0;
- t.l.l_whence = 0;
- ti = (struct tindex *)f->fc.index;
-
- if (mode == F_GETLK) {
- lck = denylock(ti->locks, &t);
- if (lck)
- *fl = lck->l;
- else
- fl->l_type = F_UNLCK;
- return 0;
- }
-
- if (t.l.l_type == F_UNLCK) {
- /* try to find the lock */
- old = &ti->locks;
- lck = *old;
- while (lck) {
- if (lck->l.l_pid == curproc->pid &&
- lck->l.l_start == t.l.l_start &&
- lck->l.l_len == t.l.l_len) {
- /* found it -- remove the lock */
- *old = lck->next;
- TRACE(("tosfs: unlocked %s: %ld + %ld",
- ti->name, t.l.l_start, t.l.l_len));
- if (flk)
- (void)Flock((int)f->devinfo, 1,
- t.l.l_start, t.l.l_len);
- /* wake up anyone waiting on the lock */
- wake(IO_Q, (long)lck);
- kfree(lck);
- break;
- }
- old = &lck->next;
- lck = lck->next;
- }
- return lck ? 0 : ENSLOCK;
- }
- TRACE(("tosfs: lock %s: %ld + %ld", ti->name,
- t.l.l_start, t.l.l_len));
- do {
- /* see if there's a conflicting lock */
- while ((lck = denylock(ti->locks, &t)) != 0) {
- DEBUG(("tosfs: lock conflicts with one held by %d",
- lck->l.l_pid));
- if (mode == F_SETLKW) {
- sleep(IO_Q, (long)lck); /* sleep a while */
- }
- else
- return ELOCKED;
- }
- /* if not, add this lock to the list */
- lck = kmalloc(SIZEOF(LOCK));
- if (!lck) return ENSMEM;
- /* see if other _FLK code might object */
- if (flk) {
- r = Flock((int)f->devinfo, 0, t.l.l_start, t.l.l_len);
- if (r) {
- kfree(lck);
- if (mode == F_SETLKW && r == ELOCKED) {
- yield();
- lck = NULL;
- }
- else
- return r;
- }
- }
- } while (!lck);
- lck->l = t.l;
- lck->l.l_pid = curproc->pid;
- lck->next = ti->locks;
- ti->locks = lck;
- /* mark the file as being locked */
- f->flags |= O_LOCK;
- return 0;
- }
- return EINVFN;
- }
-
- static long ARGS_ON_STACK
- tos_datime(f, timeptr, rwflag)
- FILEPTR *f;
- short *timeptr;
- int rwflag;
- {
- if (rwflag) {
- struct tindex *ti = (struct tindex *)f->fc.index;
- ti->valid = 0;
- }
- return Fdatime(timeptr, (int)f->devinfo, rwflag);
- }
-
- static long ARGS_ON_STACK
- tos_close(f, pid)
- FILEPTR *f;
- int pid;
- {
- LOCK *lck, **oldl;
- struct tindex *t;
- FILEPTR **old, *p;
- long r = 0;
- extern int flk; /* set in main.c */
-
- t = (struct tindex *)(f->fc.index);
- /* if this handle was locked, remove any locks held by the process
- */
- if (f->flags & O_LOCK) {
- TRACE(("tos_close: releasing locks (file mode: %x)", f->flags));
- oldl = &t->locks;
- lck = *oldl;
- while (lck) {
- if (lck->l.l_pid == pid) {
- *oldl = lck->next;
- if (flk)
- (void)Flock((int)f->devinfo, 1,
- lck->l.l_start, lck->l.l_len);
- wake(IO_Q, (long)lck);
- kfree(lck);
- } else {
- oldl = &lck->next;
- }
- lck = *oldl;
- }
- }
-
- if (f->links <= 0) {
- /* remove f from the list of open file pointers on this index */
- t->valid = 0;
- old = &t->open;
- p = t->open;
- while (p && p != f) {
- old = &p->next;
- p = p->next;
- }
- assert(p);
- *old = f->next;
- f->next = 0;
- r = Fclose((int)f->devinfo);
-
- /* if the file was marked for deletion, delete it */
- if (!t->open) {
- if (t->attr & FA_DELETE) {
- (void)Fdelete(t->name);
- t->name = 0;
- }
- }
- }
- return r;
- }
-
- /*
- * check for disk change: called by the kernel if Mediach returns a
- * non-zero value
- */
-
- long ARGS_ON_STACK
- tos_dskchng(drv)
- int drv;
- {
- char dlet;
- int i;
- struct tindex *ti;
- FILEPTR *f, *nextf;
-
- dlet = 'A' + drv;
- MEDIA_DB(("tos_dskchng(%c)", dlet));
- ti = gl_ti;
- for (i = 0; i < NUM_INDICES; i++, ti++) {
- if (ti->name && ti->name[0] == dlet) {
- #ifdef NEWWAY
- /* only free the name if this index not used by any cookie */
- if (ti->links != 0)
- ti->valid = 0;
- else
- #endif /* NEWWAY */
- {
- kfree(ti->name);
- ti->name = 0;
- }
- if (!(ti->attr & FA_DIR)) {
- nextf = ti->open;
- while ( (f = nextf) != 0 ) {
- nextf = f->next;
- f->next = 0;
- }
- }
- ti->open = 0;
- /* if there are any cookies pointing at this name, they're not
- * valid any more, so we will *want* to get an error if they're
- * used.
- */
- }
- }
- tmpindex.valid = 0;
- /*
- * OK, make sure that GEMDOS knows to look for a change if we
- * ever use this drive again.
- */
- drvchanged[drv] = 1;
- return 1;
- }
-
- #ifdef NEWWAY
- /* release/copy file cookies; these functions exist to keep
- * track of whether or not the kernel is still using a file
- */
- long
- tos_release(fc)
- fcookie *fc;
- {
- struct tindex *ti = (struct tindex *)fc->index;
-
- if (ti->links <= 0) {
- FATAL("tos_release: link count of `%s' is %d", ti->name, ti->links);
- }
- ti->links--;
- COOKIE_DB(("tos_release: %s now has %d links", ti->name, ti->links));
- return 0;
- }
-
- long
- tos_dupcookie(dest, src)
- fcookie *dest, *src;
- {
- struct tindex *ti = (struct tindex *)src->index;
-
- if (ti->links <= 0) {
- FATAL("tos_dupcookie: link count of %s is %d", ti->name, ti->links);
- }
- ti->links++;
- COOKIE_DB(("tos_dupcookie: %s now has %d links", ti->name, ti->links));
- *dest = *src;
- return 0;
- }
- #endif
-
- /*
- * utility function: sets the TOS DTA, and also records what directory
- * this was in. This is just to save us a call into the kernel if the
- * correct DTA has already been set.
- */
-
- static void
- do_setdta(dta)
- DTABUF *dta;
- {
- if (dta != lastdta) {
- Fsetdta(dta);
- lastdta = dta;
- }
- }
-
- /*
- * routines for forcing a media change on drive "drv"
- */
-
- static int chdrv;
-
- /* new Getbpb function: when this is called, all the other
- * vectors can be un-installed
- */
-
- static long ARGS_ON_STACK (*Oldgetbpb) P_((int));
- static long ARGS_ON_STACK (*Oldmediach) P_((int));
- static long ARGS_ON_STACK (*Oldrwabs) P_((int, void *, int, int, int, long));
-
- static long ARGS_ON_STACK
- Newgetbpb(d)
- int d;
- {
- if (d == chdrv) {
- MEDIA_DB(("Newgetbpb(%c)", d+'A'));
- if (Oldgetbpb == Newgetbpb) {
- MEDIA_DB(("AARGH!!! BAD BPBs"));
- }
- *((Func *)0x472L) = Oldgetbpb;
- *((Func *)0x476L) = Oldrwabs;
- *((Func *)0x47eL) = Oldmediach;
- }
- return (*Oldgetbpb)(d);
- }
-
- static long ARGS_ON_STACK
- Newmediach(d)
- int d;
- {
- if (d == chdrv) {
- MEDIA_DB(("Newmediach(%c)", d+'A'));
- return 2;
- }
- return (*Oldmediach)(d);
- }
-
- static long ARGS_ON_STACK
- Newrwabs(d, buf, a, b, c, l)
- int d;
- void *buf;
- int a, b, c;
- long l;
- {
- if (d == chdrv) {
- MEDIA_DB(("Newrwabs"));
- return E_CHNG;
- }
- return (*Oldrwabs)(d, buf, a, b, c, l);
- }
-
- static void
- force_mediach(d)
- int d;
- {
- #ifdef FSFIRST_MEDIACH
- static char fname[] = "X:\\*.*";
- #else
- long r;
- static char fname[] = "X:\\X";
- #endif
- TRACE(("tosfs: disk change drive %c", d+'A'));
- MEDIA_DB(("forcing media change on %c", d+'A'));
-
- chdrv = d;
- Oldrwabs = *((Func *)0x476L);
- Oldgetbpb = *((Func *)0x472L);
- Oldmediach = *((Func *)0x47eL);
-
- if (Oldrwabs == Newrwabs || Oldgetbpb == Newgetbpb ||
- Oldmediach == Newmediach) {
- FORCE("tosfs: error in media change code");
- } else {
- *((Func *)0x476L) = Newrwabs;
- *((Func *)0x472L) = Newgetbpb;
- *((Func *)0x47eL) = Newmediach;
- }
-
- fname[0] = d + 'A';
- MEDIA_DB(("calling GEMDOS"));
- #ifdef FSFIRST_MEDIACH
- (void)Fsfirst(fname, 8);
- #else
- r = Fopen(fname, 0);
- if (r >= 0) Fclose((int)r);
- #endif
- MEDIA_DB(("done calling GEMDOS"));
- drvchanged[d] = 0;
- if ( *((Func *)0x476L) == Newrwabs ) {
- DEBUG(("WARNING: media change not performed correctly"));
- *((Func *)0x472L) = Oldgetbpb;
- *((Func *)0x476L) = Oldrwabs;
- *((Func *)0x47eL) = Oldmediach;
- }
- }
-